package com.example.ai.util;

import com.example.ai.vo.InterviewScoreVO;
import com.example.mianshiguan.api.dto.InterviewerConfigDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONArray;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.client.HttpClientErrorException;

import java.util.*;

/**
 * AI面试评分工具类
 * 使用豆包火山API进行面试答案评分
 * 该工具类负责调用豆包火山API，根据面试答案和职位要求进行评分，并返回评分结果
 */
@Slf4j
@Component
@EnableAsync
public class AIScoreUtil {

    //从配置文件中读取豆包火山API的密钥
    @Value("${doubao.api-key}")
    private String apiKey;

    // 从配置文件中读取豆包火山API的模型ID
    @Value("${doubao.model-id}")
    private String model;

    // 从配置文件中读取豆包火山API的基础URL
    @Value("${doubao.base-url}")
    private String apiUrl;

    /** HTTP请求工具，用于发送API请求 */
    private final RestTemplate restTemplate = new RestTemplate();

    /** JSON处理工具，用于解析和生成JSON数据 */
    private final ObjectMapper objectMapper = new ObjectMapper();

    /** 最大重试次数，当API调用失败时，最多重试3次 */
    private static final int MAX_RETRIES = 3;

    /**
     * 计算面试评分
     * 根据候选人的面试答案和面试官配置信息，调用豆包火山API进行评分
     * @param answer 候选人的面试答案
     * @param config 面试官配置信息，包含职位要求、评分标准等
     * @return 评分结果，包含总分、技术能力得分、沟通能力得分、问题解决能力得分、优势评价和改进建议
     * @throws IllegalArgumentException 当面试答案为空时抛出异常
     * @throws RuntimeException 当API调用失败或解析响应失败时抛出异常
     */
    public InterviewScoreVO calculateScore(String answer, InterviewerConfigDTO config) {
        Object string = new String();
        if (answer == null || answer.trim().isEmpty()) {
            throw new IllegalArgumentException("面试答案不能为空");
        }

        log.info("开始AI评分，答案长度: {}", answer.length());

        // 构建AI请求提示词
        String prompt = buildPrompt(answer, config);
        log.info("生成的提示词: {}", prompt);

        // 尝试调用AI进行评分，最多重试3次
        Exception lastException = null;
        for (int i = 0; i < MAX_RETRIES; i++) {
            try {
                log.info("第{}次尝试调用AI评分", i + 1);
                JSONObject aiResponse = callAI(prompt, config);
                log.info("AI原始响应: {}", aiResponse);

                // 解析AI返回的评分结果
                InterviewScoreVO scoreVO = parseAIResponse(aiResponse);
                if (scoreVO != null) {
                    return scoreVO;
                }
            } catch (Exception e) {
                lastException = e;
                log.error("第{}次AI评分失败: {}", i + 1, e.getMessage());
                if (i < MAX_RETRIES - 1) {
                    try {
                        Thread.sleep(1000 * (i + 1)); // 递增延迟
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        // 如果所有重试都失败，抛出异常
        throw new RuntimeException("AI评分失败: " + (lastException != null ? lastException.getMessage() : "未知错误"));
    }

    /**
     * 更新评分结果
     * 将源评分对象的值复制到目标评分对象中
     * @param target 目标评分对象
     * @param source 源评分对象
     */
    private void updateScore(InterviewScoreVO target, InterviewScoreVO source) {
        target.setTotalScore(source.getTotalScore());
        target.setTechnicalScore(source.getTechnicalScore());
        target.setCommunicationScore(source.getCommunicationScore());
        target.setProblemSolvingScore(source.getProblemSolvingScore());
        target.setStrengths(source.getStrengths());
        target.setSuggestions(source.getSuggestions());
    }

    /**
     * 构建AI提示词
     * 根据面试答案和面试官配置信息，构建用于AI评分的提示词
     * @param answer 候选人的面试答案
     * @param config 面试官配置信息，包含职位要求、评分标准等
     * @return 格式化的提示词，包含评分标准、面试问题、职位要求和面试答案
     */
    private String buildPrompt(String answer, InterviewerConfigDTO config) {
        StringBuilder prompt = new StringBuilder();
        prompt.append("你是一个专业的面试评分专家。请严格按照以下JSON格式返回评分结果，不要包含任何其他内容：\n\n");
        prompt.append("{\n");
        prompt.append("  \"totalScore\": 85,  // 综合得分（0-100）\n");
        prompt.append("  \"technicalScore\": 90,  // 技术能力得分（0-100）\n");
        prompt.append("  \"communicationScore\": 85,  // 沟通表达得分（0-100）\n");
        prompt.append("  \"problemSolvingScore\": 88,  // 问题解决得分（0-100）\n");
        prompt.append("  \"strengths\": \"优势评价\",  // 优势评价，必须基于答案内容具体说明\n");
        prompt.append("  \"suggestions\": \"改进建议\"  // 改进建议，必须基于答案内容具体说明\n");
        prompt.append("}\n\n");

        prompt.append("评分标准：\n");
        prompt.append("1. 技术能力（0-100分）：\n");
        prompt.append("   - 答案相关性（50分）：答案是否与问题相关，是否回答了问题的核心内容\n");
        prompt.append("   - 技术准确性（30分）：技术内容是否准确，是否有技术错误\n");
        prompt.append("   - 技术深度（20分）：是否展示了深入的技术理解\n\n");

        prompt.append("2. 沟通能力（0-100分）：\n");
        prompt.append("   - 答案完整性（40分）：是否完整回答了问题的各个方面\n");
        prompt.append("   - 表达清晰度（30分）：是否表达清晰，易于理解\n");
        prompt.append("   - 逻辑性（30分）：答案是否结构清晰，逻辑连贯\n\n");

        prompt.append("3. 问题解决能力（0-100分）：\n");
        prompt.append("   - 问题理解（40分）：是否准确理解面试问题\n");
        prompt.append("   - 解决方案（30分）：是否针对问题提出解决方案\n");
        prompt.append("   - 创新性（30分）：是否展示创新思维\n\n");

        prompt.append("评分规则：\n");
        prompt.append("1. 以下情况直接判定为不及格（总分低于60分）：\n");
        prompt.append("   - 答案与问题完全无关\n");
        prompt.append("   - 答案内容完全无法理解\n");
        prompt.append("   - 没有针对面试问题给出答案\n\n");

        prompt.append("2. 以下情况判定为勉强及格（总分60-70分）：\n");
        prompt.append("   - 答案部分相关但缺乏深度\n");
        prompt.append("   - 答案中有少量技术错误\n");
        prompt.append("   - 答案结构不够清晰\n\n");

        prompt.append("3. 以下情况可以给予高分（80分以上）：\n");
        prompt.append("   - 答案完全相关且深入\n");
        prompt.append("   - 技术内容准确无误\n");
        prompt.append("   - 表达清晰，逻辑性强\n\n");

        prompt.append("面试问题：\n").append(config.getQuestion()).append("\n\n");
        prompt.append("职位要求：\n");
        prompt.append("职位：").append(config.getPositionName()).append("\n");
        prompt.append("技术能力要求：").append(config.getTechnicalRequirements()).append("\n");
        prompt.append("沟通能力要求：").append(config.getCommunicationRequirements()).append("\n");
        prompt.append("问题解决能力要求：").append(config.getProblemSolvingRequirements()).append("\n\n");

        prompt.append("面试答案：\n").append(answer).append("\n\n");

        prompt.append("请严格按照上述JSON格式返回评分结果，不要包含任何其他内容。评分时请严格遵循评分标准，重点关注答案与问题的相关性。\n");
        prompt.append("注意：strengths和suggestions字段必须基于候选人的具体答案内容来填写，不能使用模板化的评价。strengths要指出答案中具体的技术亮点和优势，suggestions要针对答案中具体存在的问题给出改进建议。\n");

        return prompt.toString();
    }

    /**
     * 调用豆包火山AI进行评分
     * 根据提示词和面试官配置信息，调用豆包火山API进行评分
     * @param prompt 评分提示词，包含评分标准、面试问题、职位要求和面试答案
     * @param config 面试官配置信息，包含职位要求、评分标准等
     * @return AI返回的响应，包含评分结果
     * @throws RuntimeException 当API调用失败时抛出异常
     */
    private JSONObject callAI(String prompt, InterviewerConfigDTO config) {
        // 打印面试官配置信息
        log.info("面试官配置信息：");
        log.info("职位名称：{}", config.getPositionName());
        log.info("技术能力要求：{}", config.getTechnicalRequirements());
        log.info("沟通能力要求：{}", config.getCommunicationRequirements());
        log.info("问题解决能力要求：{}", config.getProblemSolvingRequirements());
        log.info("提示词模板：{}", config.getPromptTemplate());
        log.info("温度参数：{}", config.getTemperature());

        // 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + apiKey);

        // 构建请求体
        JSONObject requestBody = new JSONObject();
        requestBody.put("model", model);
        requestBody.put("temperature", config.getTemperature() != null ? config.getTemperature() : 0.7);
        requestBody.put("max_tokens", 2000);

        // 构建消息列表
        JSONArray messages = new JSONArray();

        // 添加系统消息
        JSONObject systemMessage = new JSONObject();
        systemMessage.put("role", "system");
        systemMessage.put("content", "你是一个专业的面试评分助手，请根据面试答案和职位要求进行评分。");
        messages.add(systemMessage);

        // 添加用户消息
        JSONObject userMessage = new JSONObject();
        userMessage.put("role", "user");
        userMessage.put("content", prompt);
        messages.add(userMessage);

        requestBody.put("messages", messages);

        // 打印请求体
        log.info("AI请求体：{}", requestBody.toJSONString());
        log.info("API URL：{}", apiUrl + "/chat/completions");

        try {
            // 发送请求
            HttpEntity<String> request = new HttpEntity<>(requestBody.toString(), headers);
            String response = restTemplate.postForObject(apiUrl + "/chat/completions", request, String.class);
            
            if (response == null) {
                throw new RuntimeException("AI服务返回空响应");
            }

            // 打印响应
            log.info("AI响应：{}", response);

            return JSONObject.parseObject(response);
        } catch (HttpClientErrorException e) {
            log.error("HTTP错误：状态码={}, 响应体={}", e.getStatusCode(), e.getResponseBodyAsString());
            throw new RuntimeException("AI服务调用失败：" + e.getStatusCode() + " - " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("调用AI服务时发生错误：", e);
            throw new RuntimeException("调用AI服务时发生错误：" + e.getMessage());
        }
    }

    /**
     * 解析AI返回的评分结果
     * 根据AI返回的响应，解析出评分结果
     * @param aiResponse AI返回的响应，包含评分结果
     * @return 评分结果对象，包含总分、技术能力得分、沟通能力得分、问题解决能力得分、优势评价和改进建议
     * @throws RuntimeException 当解析失败或分数无效时抛出异常
     */
    private InterviewScoreVO parseAIResponse(JSONObject aiResponse) {
        try {
            // 解析豆包火山API响应
            JSONArray choices = aiResponse.getJSONArray("choices");
            if (choices == null || choices.isEmpty()) {
                throw new RuntimeException("AI响应中没有评分结果");
            }

            JSONObject choice = choices.getJSONObject(0);
            JSONObject message = choice.getJSONObject("message");
            String content = message.getString("content");

            // 处理可能的Markdown代码块
            content = content.replaceAll("```json\\s*", "").replaceAll("```\\s*$", "").trim();

            // 尝试提取JSON内容
            int jsonStart = content.indexOf('{');
            int jsonEnd = content.lastIndexOf('}');

            if (jsonStart == -1 || jsonEnd == -1) {
                throw new RuntimeException("AI响应中没有找到有效的JSON内容");
            }

            String jsonContent = content.substring(jsonStart, jsonEnd + 1);

            // 解析JSON内容
            JSONObject scoreData;
            try {
                scoreData = JSONObject.parseObject(jsonContent);
            } catch (Exception e) {
                throw new RuntimeException("解析JSON内容失败: " + e.getMessage());
            }

            // 验证必要字段
            if (!scoreData.containsKey("totalScore") || !scoreData.containsKey("technicalScore") ||
                !scoreData.containsKey("communicationScore") || !scoreData.containsKey("problemSolvingScore")) {
                throw new RuntimeException("AI响应缺少必要的评分字段");
            }

            // 验证分数是否在有效范围内
            int totalScore = scoreData.getInteger("totalScore");
            int technicalScore = scoreData.getInteger("technicalScore");
            int communicationScore = scoreData.getInteger("communicationScore");
            int problemSolvingScore = scoreData.getInteger("problemSolvingScore");

            if (!isValidScore(totalScore) || !isValidScore(technicalScore) || 
                !isValidScore(communicationScore) || !isValidScore(problemSolvingScore)) {
                throw new RuntimeException(String.format(
                    "AI返回的分数不在有效范围内: totalScore=%d, technicalScore=%d, communicationScore=%d, problemSolvingScore=%d",
                    totalScore, technicalScore, communicationScore, problemSolvingScore));
            }

            InterviewScoreVO scoreVO = new InterviewScoreVO();
            scoreVO.setTotalScore(totalScore);
            scoreVO.setTechnicalScore(technicalScore);
            scoreVO.setCommunicationScore(communicationScore);
            scoreVO.setProblemSolvingScore(problemSolvingScore);
            scoreVO.setStrengths(scoreData.getString("strengths"));
            scoreVO.setSuggestions(scoreData.getString("suggestions"));

            return scoreVO;

        } catch (Exception e) {
            log.error("解析AI响应失败: {}", e.getMessage());
            throw new RuntimeException("解析AI响应失败: " + e.getMessage(), e);
        }
    }

    /**
     * 验证分数是否在有效范围内
     * 检查分数是否在0-100之间
     * @param score 分数
     * @return 是否有效
     */
    private boolean isValidScore(int score) {
        return score >= 0 && score <= 100;
    }
} 